using PaintIsland;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Chunk : MonoBehaviour
{
    public const int CHUNK_SIZE = 16;
    public const int DIRECTIONS_COUNT = 8;
    public BlockID[,,] Data
    {
        get => data;
    }

    public EnumArray<ChunkDirection, Chunk> Neighbours
    {
        get => neighbours;
        set => neighbours = value;
    }

    public EnumArray<ChunkDirection, Chunk> neighbours;

    private BlockID[,,] data;
    private ChunkMeshGenerator mesh;

    public int blockCount { get; private set; }

    public Vector3Int ChunkLocation
    {
        get => new Vector3Int(Mathf.FloorToInt(transform.position.x / CHUNK_SIZE), Mathf.FloorToInt(transform.position.y / CHUNK_SIZE), Mathf.FloorToInt(transform.position.z / CHUNK_SIZE));
    }

    public bool deprecated;
    
    private void Awake()
    {
        Neighbours = new EnumArray<ChunkDirection, Chunk>(DIRECTIONS_COUNT);
        data = new BlockID[CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE];
        mesh = GetComponent<ChunkMeshGenerator>();
    }

    private void Start()
    {
        deprecated = false;
        mesh.GenerateMesh();
    }

    public void PlaceBlock(BlockID id, int x, int y, int z)
    {
        if (data[x, y, z] == BlockID.Bedrock) return;
        SetBlock(id, x, y, z);
    }

    public void LoadData(BlockID[,,] data)
    {
        SetBlocks(data, 0, 0, 0);
    }

    public void SetBlocks(BlockID[,,] ids, int ox, int oy, int oz)
    {
        for (int x = 0; x < ids.GetLength(0); x++)
        {
            for (int y = 0; y < ids.GetLength(1); y++)
            {
                for (int z = 0; z < ids.GetLength(2); z++)
                {
                    if (ox + x < 0 || ox + x >= CHUNK_SIZE) continue;
                    if (oy + y < 0 || oy + y >= CHUNK_SIZE) continue;
                    if (oz + z < 0 || oz + z >= CHUNK_SIZE) continue;

                    data[ox + x, oy + y, oz + z] = ids[x, y, z];
                    blockCount++;
                }
            }
        }

        ChunkUpdate();
        if (ox <= 0) neighbours[ChunkDirection.Left]?.ChunkUpdate();
        else if (ox + ids.GetLength(0) >= CHUNK_SIZE - 1) neighbours[ChunkDirection.Right]?.ChunkUpdate();

        if (oy <= 0) neighbours[ChunkDirection.Bottom]?.ChunkUpdate();
        else if (oy + ids.GetLength(1) >= CHUNK_SIZE - 1) neighbours[ChunkDirection.Top]?.ChunkUpdate();

        if (oz <= 0) neighbours[ChunkDirection.Near]?.ChunkUpdate();
        else if (oz + ids.GetLength(2) >= CHUNK_SIZE - 1) neighbours[ChunkDirection.Far]?.ChunkUpdate();
    }

    public void SetBlock(BlockID id, int x, int y, int z)
    {
        if (x < 0 || x >= CHUNK_SIZE) return;
        if (y < 0 || y >= CHUNK_SIZE) return;
        if (z < 0 || z >= CHUNK_SIZE) return;

        data[x, y, z] = id;
        blockCount++;

        ChunkUpdate();
        if (x == 0) neighbours[ChunkDirection.Left]?.ChunkUpdate();
        else if (x == CHUNK_SIZE - 1) neighbours[ChunkDirection.Right]?.ChunkUpdate();

        if (y == 0) neighbours[ChunkDirection.Bottom]?.ChunkUpdate();
        else if (y == CHUNK_SIZE - 1) neighbours[ChunkDirection.Top]?.ChunkUpdate();

        if (z == 0) neighbours[ChunkDirection.Near]?.ChunkUpdate();
        else if (z == CHUNK_SIZE - 1) neighbours[ChunkDirection.Far]?.ChunkUpdate();
    }

    public void ChunkUpdate()
    {
        mesh.GenerateMesh();
    }

    public void ClearNeighbours()
    {
        if (neighbours[ChunkDirection.Left] is Chunk left)
            left.neighbours[ChunkDirection.Right] = null;

        if (neighbours[ChunkDirection.Right] is Chunk right)
            right.neighbours[ChunkDirection.Left] = null;

        if (neighbours[ChunkDirection.Top] is Chunk top)
            top.neighbours[ChunkDirection.Bottom] = null;

        if (neighbours[ChunkDirection.Bottom] is Chunk bottom) 
            bottom.neighbours[ChunkDirection.Top] = null;

        if (neighbours[ChunkDirection.Far] is Chunk far) 
            far.neighbours[ChunkDirection.Near] = null;

        if (neighbours[ChunkDirection.Near] is Chunk near)
            near.neighbours[ChunkDirection.Far] = null;


        for (var i = 0; i < neighbours.Length; i++)
        {
            neighbours[i] = null;
        }
    }

    private void OnDrawGizmos()
    {
        var savedColor = Gizmos.color;
        Gizmos.color = Color.white;
        Gizmos.DrawWireCube(transform.position + new Vector3(CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE) / 2.0f, new Vector3(CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE));
        Gizmos.color = savedColor;
    }
}
